#!/usr/bin/env python
#-------------------------------------------------------------------------------
#!  file rest-api.py
#!  brief
#!      A very light REST api tester
#!
#!  $Id: rest_api.py 271 2014-04-13 06:19:35Z ac.verbeck@gmail.com $
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
#   Copyright and Disclaimer Notice
#
#   Copyright (c) 2012 A.C. Verbeck
#
#   This file is part of REST API: A very light REST API tester.
#
#   REST API is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   REST API is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with REST API.  If not, see <http://www.gnu.org/licenses/>.
#
#-------------------------------------------------------------------------------

__author__ = '$Author: ac.verbeck@gmail.com $'
__version__ = '$Rev: 271 $'
__date__ = '$Date: 2014-04-13 06:19:35 +0000 (Sun, 13 Apr 2014) $'
__license__ = 'MIT'

import sys
import csv
import json
import time
import requests
import logging

#-------------------------------------------------------------------------------
#   bldArgs
#   This function does two things:
#   - verifies that the header and the row have the same number of parameters
#   - converts the two lists (hdr, row) to a dictionary
#-------------------------------------------------------------------------------
def bldArgs(hdr, row):
    if len(hdr) == 0:
        return {}
    if len(hdr) != len(row):
        print "Malformed csv file",len(hdr), len(row)
        exit(1)
    return dict(zip(hdr,row))


#-------------------------------------------------------------------------------
#   testRunner
#   Run each of the RESTful API tests
#   Input is read from opt.input
#   Output is saved to opt.output
#-------------------------------------------------------------------------------
def testRunner(opt):
    f_out = open(opt.output, 'a')
    with open(opt.input, 'rb') as f_in:
        test_reader = csv.reader(f_in, dialect='excel')
        first_row = True
        for row in test_reader:
            if first_row == True:
                header = row
                del header[0:2]
                first_row = False
            else:
                if not row:
                    continue

                if row[0].lower() == 'comment':
                    cmt = '%s\n' % row[1]
                    f_out.write(cmt)
                    continue

                cmd = row[0]
                base_url = row[1]
                del row[0:2]
                logging.debug(base_url)
                f_out.write('URL: %s\n' % base_url)
                try:
                    r = requests.request(cmd, base_url, params=bldArgs(header,row), timeout=opt.timeout)
                except requests.exceptions.HTTPError as e:
                    err = "HTTPError status code: %d" % e.response.status_code
                    logging.error(err)
                    f_out.write('%s\n\n' % err)
                    continue
                except requests.exceptions.ConnectionError as e:
                    err = "ConnectionError: %s" % e
                    logging.error(err)
                    f_out.write('%s\n\n' % err)
                    continue
                except requests.exceptions.RequestException as e:
                    err = "RequestException: %s" % e
                    logging.error(err)
                    f_out.write('%s\n\n' % err)
                    continue
                except:
                    logging.error('Unexpected error: %s', sys.exc_info()[0])
                    f_out.write('Unexpected error\n\n')
                    continue

                f_out.write('Response status code: %d\n' % r.status_code)
                f_out.write('Response Header:\n{\n')
                f_out.write('\n'.join(['\t%s: %s' % (k, v) for k, v in r.headers.items()]))
                f_out.write('\n}\n')
                logging.debug(",".join(["[%s=%s]" % (k, v) for k, v in r.headers.items()]))
                f_out.write('Response:\n')
                f_out.write(r.text.encode("UTF-8"))
                f_out.write('\n\n')

                if opt.delay != 0:
                    time.sleep(opt.delay)
                
    f_out.close()


#-------------------------------------------------------------------------------
#   testLooper
#   Run the test over and over
#   Input is read from opt.input
#   Output is saved to opt.output
#-------------------------------------------------------------------------------
def testLooper(opt):
    idx=1
    while idx <= opt.loop:
        logging.debug('Pass: %d/%d', idx, opt.loop)
        testRunner(opt)
        idx += 1

#-------------------------------------------------------------------------------
#   parseArgs:
#   Parse REST api arguments:
#   -- input: input CSV file for the test run
#   -- output: output file: output from REST calls
#   -- log: log file: technical output from the REST call
#   -- loop: number of loops to run the file (all calls are repeated)
#   -- delay: how much time to delete between REST calls
#   -- timeout: how long to wait for a REST call
#-------------------------------------------------------------------------------
def parseArgs():
    import argparse
    ap = argparse.ArgumentParser(description="RESTful API test runner script (v2)")
    
    ap.add_argument("-i", "--input", default="test.csv",
                    help="Input filename (default: %(default)s)")
    ap.add_argument("-o", "--output", default="test.out",
                    help="Output filename (default: %(default)s)")
    ap.add_argument("-l", "--log", default="test.log",
                    help="Logging filename (default: %(default)s)")
    ap.add_argument("-p", "--loop", default=1, type=int,
                    help="Loop count (default is 1)")
    ap.add_argument("-d", "--delay", default=0, type=float,
                    help="Delay time between REST API calls (default is 1.0 seconds)")
    ap.add_argument("-t", "--timeout", default=4, type=float,
                    help="Timeout (default is 4.0 seconds)")

    if len(sys.argv) == 1:
        ap.print_help()
        sys.exit(1)
    return ap.parse_args()


#-------------------------------------------------------------------------------
#   logging configuration
#-------------------------------------------------------------------------------
def logInit(opt):
    fmtr='%(asctime)s | %(name)s | %(levelname)s | %(message)s'
    logging.basicConfig(level=logging.DEBUG, filename=opt.log, format=fmtr)


#-------------------------------------------------------------------------------
#   main:
#   Parse the arguments
#   Run the CSV tests
#-------------------------------------------------------------------------------
def main():
    options = parseArgs()
    logInit(options)

    logging.debug('Log Start')
    if options.loop == 1:
        testRunner(options)
    else:
        testLooper(options)
    logging.debug('Log End')

    return 0

#-------------------------------------------------------------------------------
#   Main processing
#-------------------------------------------------------------------------------
if __name__ == "__main__":
    sys.exit(main())

#
#   End: rest_api.py
#
